package com.chujian.framework.auth.token;

import cn.hutool.core.date.DateUtil;
import cn.hutool.crypto.asymmetric.RSA;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.nimbusds.jose.*;
import com.nimbusds.jose.crypto.RSASSASigner;
import com.nimbusds.jose.crypto.RSASSAVerifier;
import com.nimbusds.jose.jwk.RSAKey;
import com.chujian.framework.auth.Accessor;
import com.chujian.framework.exception.AuthException;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;

import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.Date;

@Slf4j
@Getter
@Setter
public class BaseTokenService {

    private String rsaPubKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCsnOvZXeLVagRKup6cz+/H2dcLcXHODk+8xiN+WLKPsNz0yZMnZ8kH0au3cJJnD/azyUpfjHPJcYvLP52hagL8ieeadsA9WzIVv37B8fAj2uOVn/BsA7rG5mkWZloUIfj3bIP2Il1a5czFxa7k6X83cltpmkWyr+hdpefUUovnOQIDAQAB";
    private String rsaPriKey = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKyc69ld4tVqBEq6npzP78fZ1wtxcc4OT7zGI35Yso+w3PTJkydnyQfRq7dwkmcP9rPJSl+Mc8lxi8s/naFqAvyJ55p2wD1bMhW/fsHx8CPa45Wf8GwDusbmaRZmWhQh+Pdsg/YiXVrlzMXFruTpfzdyW2maRbKv6F2l59RSi+c5AgMBAAECgYALx18EO5cCpWwCzfKMJJfaEUROhnijvSAFk5dBNEwGCGWBij5RjHBgSVdgiozg/crByLj7pW3Sx53omhAXf1CR/qEX6fbGSZyV/ksYN5aa15rjhJiW4oubwZTVWeAo1bZY4QZ8880138IiJ57nOQDPoG1SubQY7qcOpj2Q5m/NEQJBANoivQG1OQJbu7UG4OyMJz71nBLs8BoAonwK06WELA3n5lv2rzqlfFVfcb2G/NLlUF4G2za/0g5NvI+Xl125M2kCQQDKk0wBYQLLLJGWmjl3ULbIEL5BLmrwk0OJtgaqDVPh4eDk9/OT7QndDd9gZoo4s7nxkJ4LF4hrR9LWD0I60CtRAkA86eh32qBx3SX8tlAHHlgzR6juzAV8bWLyWp+hn9ghhfmZArkKGn4u5PlBILnnNqwLpcR/dG93GRQn0QAu5vHJAkEAh11QL6e0pzGcA6gA+ILd8OiHSaUL3sB5I/+XinCnecxnbHlnsZR2v9kySAedG3C1sv7xLPZHuXBgKZw+pHBQUQJAf+N/u8aBWgkt2Bm0Szto1nimKTuYFh1Ze9FK9Y/NlsAuGCjjcTvLjfPBxOVxYYf/EqDR/ktSFJjzBgPoFy9Jdw==";
    private RSAKey rsaKey;

    public BaseTokenService() {
        RSA rsa = new RSA(rsaPriKey, rsaPubKey);
        RSAPrivateKey privateKey = (RSAPrivateKey) rsa.getPrivateKey();
        RSAPublicKey publicKey = (RSAPublicKey) rsa.getPublicKey();
        rsaKey = new RSAKey.Builder(publicKey).privateKey(privateKey).build();
    }

    /**
     * 创建tokenPayload
     *
     * @param accessor accessor
     * @return Accessor
     */
    public Accessor creAccessorPayload(Accessor accessor) {
        return accessor;
    }

    /**
     * 生成Token
     *
     * @param accessor     accessor
     * @param expireSecond 有效时间：单位秒
     * @return token
     * @throws Exception e
     */
    public String creToken(Accessor accessor, Long expireSecond) throws Exception {
        JwtToken token = new JwtToken();
        BeanUtils.copyProperties(accessor, token);

        if (expireSecond == null || expireSecond <= 0L) { // 默认2天
            expireSecond = 60 * 60 * 24 * 2L;
        }

        // 时间
        Date now = new Date();
        token.setIssueTime(now.getTime()); // 签发时间
        token.setExpireSecond(expireSecond); // 有效时间

        Date exp = DateUtil.offsetSecond(now, expireSecond.intValue());
        token.setExpirationMillis(exp.getTime()); // 到期时间

        JWSHeader jwsHeader = new JWSHeader.Builder(JWSAlgorithm.RS256)
                .type(JOSEObjectType.JWT)
                .build();

        Payload payload = new Payload(JSON.toJSONString(token));
        JWSObject jwsObject = new JWSObject(jwsHeader, payload);
        JWSSigner jwsSigner = new RSASSASigner(rsaKey, true);
        jwsObject.sign(jwsSigner);

        return jwsObject.serialize();
    }

    /**
     * token解析
     *
     * @param token token
     * @param token ignoreExp 忽略过期
     * @return Accessor
     * @throws Exception e
     */
    public Accessor verifyToken(String token, boolean ignoreExpire) throws Exception {
        JWSObject jwsObject = JWSObject.parse(token);
        RSAKey publicRsaKey = rsaKey.toPublicJWK();

        JWSVerifier jwsVerifier = new RSASSAVerifier(publicRsaKey);
        if (!jwsObject.verify(jwsVerifier)) {
            throw new AuthException("token签名不正确");
        }
        String payload = jwsObject.getPayload().toString();
        JwtToken jwtToken = JSONObject.parseObject(payload, JwtToken.class);

        if (!ignoreExpire && jwtToken.getExpirationMillis() < System.currentTimeMillis()) {
            log.error("token 过期，用户id:{}", jwtToken.getUserId());
            throw new AuthException("token过期");
        }
        return jwtToken;
    }

}
